package com.yh.annotation;

import java.lang.reflect.Method;






import java.util.Arrays;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

















import com.yh.bean.Log;
import com.yh.bean.User;
import com.yh.service.LogService;
import com.yh.util.DateUtil;
import com.yh.util.JsonUtil;
import com.yh.util.UserContextUtil;

//定义一个切点切面类AOP采取注解方式 使用
@Component
@Aspect
public class SystemLogAspect {
	
	//注入service 用于把日志保存到数据库
	@Autowired
	private LogService logService;
	
	//声明一个对象本地异常日志记录对象
	private static final Logger logger = LoggerFactory.getLogger(SystemLogAspect.class);
	
	//service层切入点
	@Pointcut("@annotation(com.yh.annotation.SystemServiceLog)" )
	public void serviceAspect(){
		
	}
	
	//controller层切入点
	@Pointcut("@annotation(com.yh.annotation.SystemControllerLog)")
	public void controllerAspect(){
		
	}
	
	//定义一个方法用来获取controller层方法的描述信息
	
	@SuppressWarnings("rawtypes")
	public static String getControllerMethodsDescription(JoinPoint joinPoint) throws Exception{
		
		//1.获取切入目标的名称
		String targetName = joinPoint.getTarget().getClass().getName();
		System.out.println("获取切入目标的名称"+targetName);
		//2.获取切入方法的名称
		String methodName = joinPoint.getSignature().getName();
		System.out.println("获取切入方法的名称"+methodName);
		//3.获取切入的各种参数信息
		Object[] agruments=joinPoint.getArgs();
		System.out.println("获取各种参数信息"+Arrays.toString(agruments));
		//4.通过获取到切入的名称 利用反射得到对应的类
		Class targetClass=Class.forName(targetName);
		System.out.println("通过获取到切入的名称 利用反射得到对应的类"+targetClass);
		//5.通过类得到这个类所有的方法数组
		Method[] methods=targetClass.getMethods();
		System.out.println("通过类得到这个类所有的方法数组"+Arrays.toString(methods));
		String description ="";
		
		for (Method method : methods) {
			System.out.println(method.getName()+"================================================");
			if(method.getName().equals(methodName)){
			Class[] clazzs=method.getParameterTypes();
			System.out.println("clazzs的长度是"+clazzs.length+"内容是"+Arrays.toString(clazzs));
			
			 if (clazzs.length==agruments.length) {
				description= method.getAnnotation(SystemControllerLog.class).description();
				break;
			}
			}
		}
		return description;
		
	}
	
	
	//定义一个方法用来获取service层方法的描述信息
		
		@SuppressWarnings("rawtypes")
		public static String getServiceMethodsDescription(JoinPoint joinPoint) throws Exception{
			
			//1.获取切入目标的名称
			String targetName = joinPoint.getTarget().getClass().getName();
			//2.获取切入方法的名称
			String methodName = joinPoint.getSignature().getName();
			//3.获取切入的各种参数信息
			Object[] agruments=joinPoint.getArgs();
			//4.通过获取到切入的名称 利用反射得到对应的类
			Class targetClass=Class.forName(targetName);
			//5.通过类得到这个类所有的方法数组
			Method[] methods=targetClass.getMethods();
			String description ="";
			
			for (Method method : methods) {
				if(method.getName().equals(methodName)){
				Class[] clazzs=method.getParameterTypes();
				
				 if (clazzs.length==agruments.length) {
					description= method.getAnnotation(SystemServiceLog.class).description();
				    break;
				 }
				}
			}
			return description;
			
		}
		
	//定义前置通知记录用户操作
	
	@Before(value="controllerAspect()")
	public  void doBefore(JoinPoint joinPoint){
		//1.获取上下文的request 对象 通过contextholder.get
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();    
        //2.获取当前session对象
		HttpSession session = request.getSession();
		//3.获取session中放入的当前登录用户,调用工具常量k 就可以获取用户登录时放入的session 对象
       User user= (User) session.getAttribute(UserContextUtil.LOGIN_USER);
       //4.获取当前用户请求的IP
       String ip = request.getRemoteAddr();
   
       ip=ip.equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip;
       System.out.println(ip);
       try {    
           //*========控制台输出=========*//    
           System.out.println("=====前置通知开始=====");    
           System.out.println("请求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));    
           System.out.println("方法描述:" + getControllerMethodsDescription(joinPoint));    
           System.out.println("请求人:" + user.getUser_name());
           System.out.println("请求IP:" + ip);    
           //*========数据库日志=========*//    
           Log log = new Log();    
           log.setDescription(getControllerMethodsDescription(joinPoint));    
           log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));    
           log.setType(0);    
           log.setRequestIp(ip);    
           log.setExceptionCode( null);    
           log.setExceptionDetail( null);    
           log.setParams( null);    
           log.setCreateBy(user.getUser_name());    
           log.setCreateDate(DateUtil.getCurrentDate());    
           //保存数据库    
          boolean result= logService.addLogInfo(log);    
           System.out.println("=====前置通知结束====="+result+"====插入成功====");    
       }  catch (Exception e) {    
           //记录本地异常日志    
           logger.error("==前置通知异常==");    
           logger.error("异常信息:{}", e.getMessage());    
       }    
        
		}
	
	//定义后置异常通知，在service 层，如果 发生异常将会被记录
	@AfterThrowing(pointcut="serviceAspect()" ,throwing = "e")
	public void doAfterThrowing(JoinPoint joinPoint,Throwable e){
		HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
          HttpSession session = request.getSession();
          //1.读取session中的用户
          User user = (User) session.getAttribute(UserContextUtil.LOGIN_USER);
          //2.获取请求ip
          String ip = request.getRemoteAddr();
          //3.获取用户请求的方法参数并序列化json格式字符串
          String params="";
          //4.获取切点方法的参数信息，并遍历所有参数信息，转化为json 字符串
          if(joinPoint.getArgs() !=null && joinPoint.getArgs().length >0){
        	  for (int i = 0; i < joinPoint.getArgs().length; i++) {
				params += JsonUtil.toJsonString(joinPoint.getArgs()[i])+";";
			}
        	  
          }
          
          try {    
              /*========控制台输出=========*/    
            System.out.println("=====异常通知开始=====");    
            System.out.println("异常代码:" + e.getClass().getName());    
            System.out.println("异常信息:" + e.getMessage());    
            System.out.println("异常方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));    
            System.out.println("方法描述:" + getServiceMethodsDescription(joinPoint));    
            System.out.println("请求人:" + user.getUser_name());    
            System.out.println("请求IP:" + ip);    
            System.out.println("请求参数:" + params);    
               /*==========数据库日志=========*/    
            Log log = new Log();  
            log.setDescription(getServiceMethodsDescription(joinPoint));    
            log.setExceptionCode(e.getClass().getName());    
            log.setType(1);    
            log.setExceptionDetail(e.getMessage());    
            log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));    
            log.setParams(params);    
            log.setCreateBy(user.getUser_name());    
            log.setCreateDate(DateUtil.getCurrentDate());    
            log.setRequestIp(ip);    
            //保存数据库    
            logService.addLogInfo(log);    
            System.out.println("=====异常通知结束=====");    
        }  catch (Exception ex) {    
            //记录本地异常日志    
            logger.error("==异常通知异常==");    
            logger.error("异常信息:{}", ex.getMessage());    
        }    
         /*==========记录本地异常日志==========*/    
        logger.error("异常方法:{}异常代码:{}异常信息:{}参数:{}", joinPoint.getTarget().getClass().getName() + joinPoint.getSignature().getName(), e.getClass().getName(), e.getMessage(), params);    
    
    }

	
	
	}
	

	

